package com.cloudservice.ccs.websocket;

import com.cloudservice.ccs.biz.ISessionBiz;
import com.cloudservice.ccs.biz.ISessionMessageBiz;
import com.cloudservice.ccs.entity.SessionMessage;
import com.cloudservice.ccs.exception.BaseException;
import com.cloudservice.ccs.exception.SessionException;
import com.cloudservice.ccs.exception.SessionMessageException;
import com.cloudservice.ccs.exception.SessionMessageServerException;
import com.cloudservice.ccs.model.ApiResult;
import com.cloudservice.ccs.util.SpringContextUtils;
import com.cloudservice.ccs.util.WebSocketSessionPool;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/session/{id}/{side}/message")
@Component
public class SessionMessageServer {

    private static final String CUSTOMER_SERVICE_SIDE = "customer-service";

    private static final String VISITOR_SIDE = "visitor";

    private static WebSocketSessionPool<Integer> customerServiceSessionPool = new WebSocketSessionPool<>();

    private static WebSocketSessionPool<Integer> visitorSessionPool = new WebSocketSessionPool<>();

    private final Logger logger = LoggerFactory.getLogger(SessionMessageServer.class);

    private ISessionBiz sessionBiz;

    private ISessionMessageBiz sessionMessageBiz;

    private boolean openingWithInvalidSide = false;

    public SessionMessageServer() {
        sessionBiz = SpringContextUtils.getBean(ISessionBiz.class);
        sessionMessageBiz = SpringContextUtils.getBean(ISessionMessageBiz.class);
    }

    public static WebSocketSessionPool<Integer> getCustomerServiceSessionPool() {
        return customerServiceSessionPool;
    }

    public static WebSocketSessionPool<Integer> getVisitorSessionPool() {
        return visitorSessionPool;
    }

    @OnOpen
    public void onOpen(@PathParam("id") Integer id, @PathParam("side") String side, Session session) throws IOException {
        try {
            sessionBiz.getOne(id);
        } catch (SessionException e) {
            session.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(ApiResult.failureWithData("会话不存在")));
            session.close();
        }
        if (CUSTOMER_SERVICE_SIDE.equals(side)) {
            customerServiceSessionPool.addSession(id, session);
        } else if (VISITOR_SIDE.equals(side)) {
            visitorSessionPool.addSession(id, session);
        } else {
            openingWithInvalidSide = true;
            session.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(ApiResult.failureWithData("错误的 side 值：" + side)));
            session.close();
        }
    }

    @OnClose
    public void onClose(@PathParam("id") Integer id, @PathParam("side") String side) {
        if (CUSTOMER_SERVICE_SIDE.equals(side)) {
            customerServiceSessionPool.removeSession(id);
        } else if (VISITOR_SIDE.equals(side)) {
            visitorSessionPool.removeSession(id);
        } else {
            if (openingWithInvalidSide) {
                openingWithInvalidSide = false;
            } else {
                logger.error("Unexpected connected client side: " + side);
            }
        }
    }

    @OnMessage
    public void onMessage(@PathParam("id") Integer id, @PathParam("side") String side, String message) throws IOException {
        try {
            SessionMessage sessionMessage = new ObjectMapper().readValue(message, SessionMessage.class);
            sessionMessage.setSessionId(id);
            com.cloudservice.ccs.entity.Session session = sessionBiz.getOne(id);
            sessionMessage.setCustomerServiceId(session.getCustomerServiceId());
            sessionMessage.setVisitorIp(session.getVisitorIp());
            if (!CUSTOMER_SERVICE_SIDE.equals(side) && !VISITOR_SIDE.equals(side)) {
                throw new SessionMessageServerException("错误的 side 值：" + side);
            }
            sessionMessage.setFromVisitor(VISITOR_SIDE.equals(side));
            sessionMessageBiz.save(sessionMessage);
        } catch (Exception e) {
            ApiResult result = ApiResult.failure();
            if (e instanceof BaseException) {
                String error = ((BaseException) e).getError();
                if (error != null) {
                    result.setError(error);
                }
                result.setData("消息发送失败：" + e.getMessage());
            } else {
                result.setData("消息发送失败");
            }
            if (CUSTOMER_SERVICE_SIDE.equals(side)) {
                customerServiceSessionPool.sendMessage(id, result);
            } else if (VISITOR_SIDE.equals(side)) {
                visitorSessionPool.sendMessage(id, result);
            } else {
                logger.error("Unexpected connected client side: " + side);
            }
            logError(e);
        }
    }

    @OnError
    public void onError(Throwable throwable) {
        logError(throwable);
    }

    private void logError(Throwable throwable) {
        logger.warn(throwable.getClass().getName() + ": " + throwable.getMessage());
        if (logger.isDebugEnabled()) {
            throwable.printStackTrace();
        }
    }
}
